﻿using System;
using RestSharp;
using Web.Base.EventArgs;
using Ayatta.Contract.Domain;
using Ayatta.Contract.Component;
using System.Collections.Specialized;

namespace Web.Base.OAuth
{
    internal abstract class OAuthClient : IOAuthClient
    {
        protected const string AccessTokenKey = "access_token";
        protected const string RefreshTokenKey = "refresh_token";
        protected const string ExpiresInKey = "expires_in";
        protected const string ScopeKey = "scope";
        protected const string ErrorKey = "error";

        protected IRestClient Client { get; private set; }
        protected OAuthConfig Config { get; private set; }

        /// <summary>
        /// State (any additional information that was provided by application and is posted back by service).
        /// </summary>
        public string State { get; private set; }

        public event EventHandler<EventArgs<ExternalUser>> Authorized;

        /// <summary>
        /// Initializes a new instance of the <see cref="OAuthClient"/> class.
        /// </summary>
        /// <param name="config">The configuration.</param>
        protected OAuthClient(OAuthConfig config)
        {
            Config = config;
            Client = new RestClient(config.BaseUri);
        }

        /// <summary>
        /// 用户成功授权后触发事件
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnAuthorized(EventArgs<ExternalUser> e)
        {
            var handler = Authorized;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        /// <summary>
        /// Returns URI of service which should be called in order to start authentication process.
        /// This URI should be used for rendering login link.
        /// </summary>
        /// <remarks>
        /// Any additional information that will be posted back by service.
        /// </remarks>
        public string GetLoginUri(string state = null)
        {
            var request = new RestRequest(Config.AuthorizationCodeResource);

            request.AddObject(new {
                response_type = "code",
                client_id = Config.ClientId,
                redirect_uri = Config.RedirectUri,
                scope = Config.Scope,
                state
            });

            return Client.BuildUri(request).ToString();
        }

        /// <summary>
        /// Obtains user information using OAuth2 service and
        /// data provided via callback request.
        /// </summary>
        /// <param name="parameters">Callback request payload (parameters).</param>
        public SkResult<ExternalUser> Callback(NameValueCollection parameters)
        {
            State = parameters["state"];
            var error = parameters["error"];
            var result = new SkResult<ExternalUser>();
            if (!string.IsNullOrEmpty(error))
            {
                result.Message = error;
                result.Extra = Config.ClientName;
            }
            var request = new RestRequest(Config.AccessTokenResource, Method.POST);
            request.AddObject(new {
                code = parameters["code"],
                client_id = Config.ClientId,
                client_secret = Config.ClientSecret,
                redirect_uri = Config.RedirectUri,
                grant_type = "authorization_code"
            });

            var response = Client.Execute(request);

            result = Callback(response);

            if (result.Status)
            {
                var guid = Guid.NewGuid().ToString().ToUpper();
                result.Data.Guid = guid;
                result.Data.CreatedOn = DateTime.Now;
                result.Data.CreatedBy = Config.ClientName;
                OnAuthorized(new EventArgs<ExternalUser>(result.Data));
            }
            return result;
        }

        protected abstract SkResult<ExternalUser> Callback(IRestResponse response);

    }
}